# gcg Project
# GHDL Code Gen (gcg) - (http://code.google.com/p/gcg/)
# 
# Rogério A. Gonçalves
# rogerio.rag@gmail.com
# http://rag.pro.br
#
# The Makefile and templates were created to facilitate the creation and execution of VHDL projects in GHDL.

# source dir.
SRCDIR = src
# tests dir.
TBDIR = testbench
# simulation dir.
SIMDIR = simulation

# vhdl files.
FILES = src/*
VHDLEX = .vhd
 
# testbench (test file).
TESTBENCHPATH = $(TBDIR)/${TESTBENCH}$(VHDLEX)
 
# Configuration of GHDL.
GHDL_CMD = ghdl
GHDL_FLAGS  = --std=02 --ieee=synopsys --warn-no-vital-generic

# Parameters for simulations on GHDL.
# GHDL_SIM_OPT = --assert-level=error --trace-signals --disp-tree --stats --stop-time=1000ns
GHDL_SIM_OPT = --assert-level=error --stop-time=1000ns

# Wave form viewer. (timing diagrams).
WAVEFORM_VIEWER = gtkwave

# Wave form speaker. (timing diagrams).
WAVEFORM_SPEAKER = python3 wavevox.py

# Auxiliary commands.
RM_CMD = rm -rf
MKDIR_CMD = mkdir
CAT_CMD = cat
USE_INF="Use make new PROJECT=entityName ARCH=ArchitectureType IN=port1,port2,portN OUT=port1,port2,portN."

# Type that supports components declaration.
TYPE_WITH_COMPONENTS=estrutural
TYPE_WITH_COMPONENTS_EN=structural

# Functions.
# Add header in the files.
define criarcabecalho
	@echo " Creating file: $2"
	@echo "-- $1 generated by script." > $2
	@echo "-- Date: `date +%a,%d/%m/%Y-%H:%M:%S`" >> $2
	@echo "-- Author: $(USERNAME)" >> $2
	@echo "-- Comments: $3." >> $2
	@echo " " >> $2
endef

# Targets of make.
all: compile run view

# Targets of make (accessible).
allacc: compile run viewacc

checktb :
ifeq ($(strip $(TESTBENCH)),)
		@echo " Variable TESTBENCH not defined. Use make compile TESTBENCH=entityname_tb for define it."
		@exit 2
endif

checkvars :
ifeq ($(strip $(PROJECT)),)
	@echo " Variable PROJECT not defined. $(USE_INF)"
	@exit 2
endif
ifeq ($(strip $(ARCH)),)
	@echo " Variable ARCH not defined. $(USE_INF)"
	@exit 2
endif
ifeq ($(strip $(IN)),)
	@echo " Variable IN not defined. $(USE_INF)"
	@exit 2
endif
ifeq ($(strip $(OUT)),)
	@echo " Variable OUT not defined. $(USE_INF)"
	@exit 2
endif

checkdirs :
# Check it directories exists, otherwise make dirs.
	@echo "Making Project Diretories:: $(SRCDIR) $(TBDIR) $(SIMDIR)"
ifeq (,$(findstring $(SRCDIR),$(wildcard $(SRCDIR) )))
	$(MKDIR_CMD) $(SRCDIR)
endif	
ifeq (,$(findstring $(TBDIR),$(wildcard $(TBDIR) )))
	$(MKDIR_CMD) $(TBDIR)
endif
ifeq (,$(findstring $(SIMDIR),$(wildcard $(SIMDIR) )))
	$(MKDIR_CMD) $(SIMDIR)
endif	

compile : checktb
	@echo "Compiling..."
	mkdir -p $(SIMDIR)
	$(GHDL_CMD) -i $(GHDL_FLAGS) --workdir=$(SIMDIR) --work=work $(TESTBENCHPATH) $(FILES)
	$(GHDL_CMD) -m  $(GHDL_FLAGS) --workdir=$(SIMDIR) --work=work $(TESTBENCH)
	@mv $(TESTBENCH) $(SIMDIR)/$(TESTBENCH)                                                                                
 
run : checktb
	@echo "Running..."
	@$(SIMDIR)/$(TESTBENCH) $(GHDL_SIM_OPT) --vcdgz=$(SIMDIR)/$(TESTBENCH).vcdgz                                      
 
view : checktb
	@echo "Viewing on ${WAVEFORM_VIEWER}..."
	gunzip --stdout $(SIMDIR)/$(TESTBENCH).vcdgz | $(WAVEFORM_VIEWER) --vcd

viewacc : checktb
	@echo "Hearing on ${WAVEFORM_SPEAKER}..."
	gunzip --stdout $(SIMDIR)/$(TESTBENCH).vcdgz > $(SIMDIR)/$(TESTBENCH).vcd 
	$(WAVEFORM_SPEAKER) $(SIMDIR)/$(TESTBENCH).vcd
 
clean :
	@echo "Clening..."
	$(GHDL_CMD) --clean --workdir=$(SIMDIR)
	$(RM_CMD) $(SIMDIR)

new : checkvars checkdirs
	@echo "Building the project ${PROJECT}"

# Creating entity and testbench files.
	$(call criarcabecalho,"Project","$(SRCDIR)/$(PROJECT)$(VHDLEX)","Entity Description: $(PROJECT)")
	@sed -e 's/<<ENTITY_NAME>>/$(PROJECT)/g' templates/entity.vhd | sed -e 's/<<IN_P>>/$(shell echo "$(IN)" | sed 's/ //g' | sed -e 's/[a-zA-Z_$$][a-zA-Z0-9_$$]*/&/g' | sed 's/,/, /g')/g' | sed -e 's/<<OUT_P>>/$(shell echo "$(OUT)" | sed 's/ //g' | sed -e 's/[a-zA-Z_$$][a-zA-Z0-9_$$]*/&/g' | sed 's/,/, /g')/g' | sed -e 's/<<ARCH_TYPE>>/$(ARCH)/g' >> $(SRCDIR)/$(PROJECT)$(VHDLEX)

# It ARCH type is structural or estrutural the components declaration is included.
ifeq ($(strip $(ARCH)),$(filter $(strip $(ARCH)),$(TYPE_WITH_COMPONENTS) $(TYPE_WITH_COMPONENTS_EN)))
	@echo " Detected ARCH=$(ARCH)."
	@echo "  Declaring components."
	@sed -i 's/<<DECL_COMPONENTS>>/$(shell cat templates/decl_components.vhd)/g' $(SRCDIR)/$(PROJECT)$(VHDLEX)
	@sed -i 's/<<DECL_SIGNALS>>/$(shell cat templates/decl_signals.vhd)/g' $(SRCDIR)/$(PROJECT)$(VHDLEX)
	@sed -i 's/<<DECL_COMP_INSTANCES>>/$(shell cat templates/decl_components_instances.vhd)/g' $(SRCDIR)/$(PROJECT)$(VHDLEX)
else
	@sed -i 's/<<DECL_COMPONENTS>>//g' $(SRCDIR)/$(PROJECT)$(VHDLEX)
	@sed -i 's/<<DECL_SIGNALS>>//g' $(SRCDIR)/$(PROJECT)$(VHDLEX)
	@sed -i 's/<<DECL_COMP_INSTANCES>>//g' $(SRCDIR)/$(PROJECT)$(VHDLEX)
endif

	$(call criarcabecalho,"Testbench","$(TBDIR)/$(PROJECT)_tb$(VHDLEX)","Test of $(PROJECT) entity.")
	@sed -e 's/<<ENTITY_NAME>>/$(PROJECT)/g' templates/entity_tb.vhd | sed -e 's/<<IN_P>>/$(shell echo "$(IN)" | sed 's/ //g' | sed -e 's/[a-zA-Z_$$][a-zA-Z0-9_$$]*/&/g' | sed 's/,/, /g')/g' | sed -e 's/<<OUT_P>>/$(shell echo "$(OUT)" | sed 's/ //g' | sed -e 's/[a-zA-Z_$$][a-zA-Z0-9_$$]*/&/g' | sed 's/,/, /g')/g' | sed -e 's/<<ARCH_TYPE>>/$(ARCH)/g' | sed -e 's/<<s_t_sinais>>/$(shell echo "$(IN),$(OUT)" | sed 's/ //g' | sed 's/^/s_t_/' | sed 's/,/, s_t_/g')/g' >> $(TBDIR)/$(PROJECT)_tb$(VHDLEX)
	@sed -i 's/<<port_map_entity_tb>>/$(shell echo "$(IN),$(OUT)" | sed 's/ //g' | sed -e 's/[a-zA-Z_$$][a-zA-Z0-9_$$]*/ &=>s_t_&/g')/g' $(TBDIR)/$(PROJECT)_tb$(VHDLEX) 
	@sed -i 's/<<record_in_vars>>/$(shell echo "$(IN)" | sed 's/ //g' | sed -e 's/[a-zA-Z_$$][a-zA-Z0-9_$$]*/ vi_&/g')/g' $(TBDIR)/$(PROJECT)_tb$(VHDLEX) 
	@sed -i 's/<<record_out_vars>>/$(shell echo "$(OUT)" | sed 's/ //g' | sed -e 's/[a-zA-Z_$$][a-zA-Z0-9_$$]*/ vo_&/g')/g' $(TBDIR)/$(PROJECT)_tb$(VHDLEX)
	@sed -i 's/<<case_test_model>>/$(shell echo "$(IN),$(OUT)" | sed 's/ //g' | sed 's/,/, /g' | wc -w)/g' $(TBDIR)/$(PROJECT)_tb$(VHDLEX)
	@sed -i 's/<<inputs_signals_injection>>/$(shell echo "$(IN)" | sed 's/ //g' | sed -e 's/[a-zA-Z_$$][a-zA-Z0-9_$$]*/s_t_& <= patterns(i).vi_&;\\n\\t\\t\\t/g' | sed 's/,//g')/g' $(TBDIR)/$(PROJECT)_tb$(VHDLEX)
	@sed -i 's/<<asserts_vars>>/$(shell echo "$(OUT)" | sed 's/ //g' | sed -e 's/[a-zA-Z_$$][a-zA-Z0-9_$$]*/assert (s_t_& = patterns(i).vo_&)\\treport "Valor de s_t_& não confere com o resultado esperado." severity error;\\n\\t\\t\\t/g' )/g' $(TBDIR)/$(PROJECT)_tb$(VHDLEX)
	
	@sed -i 's/<<params_in>>/$(shell echo "$(IN)" | sed 's/ //g' | sed -e 's/[a-zA-Z_$$][a-zA-Z0-9_$$]*/ pi_s_t_&/g')/g' $(TBDIR)/$(PROJECT)_tb$(VHDLEX) 
	@sed -i 's/<<params_out>>/$(shell echo "$(OUT)" | sed 's/ //g' | sed -e 's/[a-zA-Z_$$][a-zA-Z0-9_$$]*/ po_s_t_&/g')/g' $(TBDIR)/$(PROJECT)_tb$(VHDLEX)
	@sed -i 's/<<params_exp>>/$(shell echo "$(OUT)" | sed 's/ //g' | sed -e 's/[a-zA-Z_$$][a-zA-Z0-9_$$]*/ pe_&/g')/g' $(TBDIR)/$(PROJECT)_tb$(VHDLEX) 
	
	@sed -i 's/<<print_in_var_values>>/$(shell echo "$(IN)" | sed 's/ //g' | sed 's/,/ /g' | sed -e 's/[a-zA-Z_$$][a-zA-Z0-9_$$]*/write(line_out, string<<SQUOTE>>(" s_t_&: "));\\n\\twrite(line_out, pi_s_t_&);\\n\\t/g')/g' $(TBDIR)/$(PROJECT)_tb$(VHDLEX) 
	@sed -i "s/<<SQUOTE>>/\'/g" $(TBDIR)/$(PROJECT)_tb$(VHDLEX) 
	
	@sed -i 's/<<print_out_var_values>>/$(shell echo "$(OUT)" | sed 's/ //g' | sed 's/,/ /g' | sed -e 's/[a-zA-Z_$$][a-zA-Z0-9_$$]*/write(line_out, string<<SQUOTE>>(" s_t_&: "));\\n\\twrite(line_out, string<<SQUOTE>>("(generated: "));\\n\\twrite(line_out, po_s_t_&);\\n\\twrite(line_out, string<<SQUOTE>>(", expected: "));\\n\\twrite(line_out, pe_&);\\n\\twrite(line_out, string<<SQUOTE>>(")"));\\n\\t/g')/g' $(TBDIR)/$(PROJECT)_tb$(VHDLEX) 
	@sed -i "s/<<SQUOTE>>/\'/g" $(TBDIR)/$(PROJECT)_tb$(VHDLEX)
		
	@sed -i 's/<<compare_gen_exp>>/$(shell echo "$(OUT)" | sed 's/ //g' | sed 's/,/ /g' | sed -e 's/[a-zA-Z_$$][a-zA-Z0-9_$$]*/(s_t_& = pe_&) and/g' | sed -e 's/\(.*\)and/\1/g')/g' $(TBDIR)/$(PROJECT)_tb$(VHDLEX)

	@sed -i 's/<<params_in_print_call>>/$(shell echo "$(IN)" | sed 's/ //g' | sed -e 's/[a-zA-Z_$$][a-zA-Z0-9_$$]*/ s_t_&/g')/g' $(TBDIR)/$(PROJECT)_tb$(VHDLEX) 
	@sed -i 's/<<params_out_print_call>>/$(shell echo "$(OUT)" | sed 's/ //g' | sed -e 's/[a-zA-Z_$$][a-zA-Z0-9_$$]*/ s_t_&/g')/g' $(TBDIR)/$(PROJECT)_tb$(VHDLEX)
	@sed -i 's/<<params_exp_print_call>>/$(shell echo "$(OUT)" | sed 's/ //g' | sed -e 's/[a-zA-Z_$$][a-zA-Z0-9_$$]*/ patterns(i).vo_&/g')/g' $(TBDIR)/$(PROJECT)_tb$(VHDLEX) 
	
	
	@echo "Project $(PROJECT) created successfully."
	@echo "Done."
